#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

/* 日志器模块
    1.抽象日志器基类
    2.派生不同的子类(同步日志器 & 异步日志器)
*/
#ifndef _GUN_SOURCE
#define _GUN_SOURCE
#endif

#define DEFAULT_LOGGER "default-logger"

#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <mutex>
#include <atomic>
#include <cstdarg>
#include <unordered_map>

namespace log {
    class Logger {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(std::string &logger_name, 
            LogLevel::Value limit_level, 
            Formatter::ptr &formatter,
            std::vector<LogSink::ptr> &sinks)
            :logger_name_(logger_name),
            limit_level_(limit_level),
            formatter_(formatter),
            sinks_(sinks.begin(), sinks.end()) {}
    public:
        const std::string& get_name() { return logger_name_; }
        void debug(const std::string &file, size_t line, const std::string &format, ...) {
            // 1.判断日志输出等级与限制等级关系，高于才能输出，低于就不再继续
            if (LogLevel::Value::DEBUG < limit_level_) return;
            // 2.对format格式化字符串和不定参进行字符串组织，得到日志消息字符串
            va_list ap;
            va_start(ap, format);
            char *res = nullptr;
            int n = vasprintf(&res, format.c_str(), ap);
            if (n == -1) {
                std::cout << "vasprintf() 不定参解析错误!\n";
                return;
            }
            va_end(ap);// ap指针置空
            /*
            // 3.构造LogMsg对象
            // 4.通过格式化工具对LogMsg对象进行格式化，得到格式化后的日志字符串
            // 5.进行日志落地
            */
            serialize(LogLevel::Value::DEBUG, file, line, res);
            free(res);// 释放申请的空间
        }
        void info(const std::string &file, size_t line, const std::string &format, ...) {
            // 1.判断日志输出等级与限制等级关系，高于才能输出，低于就不再继续
            if (LogLevel::Value::INFO < limit_level_) return;
            // 2.对format格式化字符串和不定参进行字符串组织，得到日志消息字符串
            va_list ap;
            va_start(ap, format);
            char *res = nullptr;
            int n = vasprintf(&res, format.c_str(), ap);
            if (n == -1) {
                std::cout << "vasprintf() 不定参解析错误!\n";
                return;
            }
            va_end(ap);// ap指针置空
            /*
            // 3.构造LogMsg对象
            // 4.通过格式化工具对LogMsg对象进行格式化，得到格式化后的日志字符串
            // 5.进行日志落地
            */
            serialize(LogLevel::Value::INFO, file, line, res);
            free(res);// 释放申请的空间
        }
        void warn(const std::string &file, size_t line, const std::string &format, ...) {
            // 1.判断日志输出等级与限制等级关系，高于才能输出，低于就不再继续
            if (LogLevel::Value::WARN < limit_level_) return;
            // 2.对format格式化字符串和不定参进行字符串组织，得到日志消息字符串
            va_list ap;
            va_start(ap, format);
            char *res = nullptr;
            int n = vasprintf(&res, format.c_str(), ap);
            if (n == -1) {
                std::cout << "vasprintf() 不定参解析错误!\n";
                return;
            }
            va_end(ap);// ap指针置空
            /*
            // 3.构造LogMsg对象
            // 4.通过格式化工具对LogMsg对象进行格式化，得到格式化后的日志字符串
            // 5.进行日志落地
            */
            serialize(LogLevel::Value::WARN, file, line, res);
            free(res);// 释放申请的空间
        }
        void error(const std::string &file, size_t line, const std::string &format, ...) {
            // 1.判断日志输出等级与限制等级关系，高于才能输出，低于就不再继续
            if (LogLevel::Value::ERROR < limit_level_) return;
            // 2.对format格式化字符串和不定参进行字符串组织，得到日志消息字符串
            va_list ap;
            va_start(ap, format);
            char *res = nullptr;
            int n = vasprintf(&res, format.c_str(), ap);
            if (n == -1) {
                std::cout << "vasprintf() 不定参解析错误!\n";
                return;
            }
            va_end(ap);// ap指针置空
            /*
            // 3.构造LogMsg对象
            // 4.通过格式化工具对LogMsg对象进行格式化，得到格式化后的日志字符串
            // 5.进行日志落地
            */
            serialize(LogLevel::Value::ERROR, file, line, res);
            free(res);// 释放申请的空间
        }
        void fatal(const std::string &file, size_t line, const std::string &format, ...) {
            // 1.判断日志输出等级与限制等级关系，高于才能输出，低于就不再继续
            if (LogLevel::Value::FATAL < limit_level_) return;
            // 2.对format格式化字符串和不定参进行字符串组织，得到日志消息字符串
            va_list ap;
            va_start(ap, format);
            char *res = nullptr;
            int n = vasprintf(&res, format.c_str(), ap);
            if (n == -1) {
                std::cout << "vasprintf() 不定参解析错误!\n";
                return;
            }
            va_end(ap);// ap指针置空
            /*
            // 3.构造LogMsg对象
            // 4.通过格式化工具对LogMsg对象进行格式化，得到格式化后的日志字符串
            // 5.进行日志落地
            */
            serialize(LogLevel::Value::FATAL, file, line, res);
            free(res);// 释放申请的空间
        }

        virtual void log(const char *data, size_t length) = 0;
    protected:
        void serialize(LogLevel::Value level, const std::string &file, size_t line, char *res) {
            // 3.构造LogMsg对象
            LogMsg msg(level, file, line, res, logger_name_);
            // 4.通过格式化工具对LogMsg对象进行格式化，得到格式化后的日志字符串
            std::stringstream ss;
            formatter_->format(ss, msg);
            // 5.进行日志落地
            log(ss.str().c_str(), ss.str().size());
        }
        std::mutex mtx_;// 互斥锁，日志落地时需要保证互斥，即向标准输出/文件/数据库(临界资源)写入消息时都要互斥
        std::string logger_name_;// 日志器名称，是日志器的唯一标识
        std::atomic<LogLevel::Value> limit_level_;// 日志输出限制等级，原子变量用于提高访问效率
        Formatter::ptr formatter_;// 格式化器
        std::vector<LogSink::ptr> sinks_;// 日志落地数组
    };

    class SyncLogger : public Logger {
    public:
        SyncLogger(std::string &logger_name, 
            LogLevel::Value limit_level, 
            Formatter::ptr &formatter,
            std::vector<LogSink::ptr> &sinks)
            : Logger(logger_name, limit_level, formatter, sinks) {}
        virtual void log(const char *data, size_t length) override {
            std::unique_lock<std::mutex> lock(mtx_);
            if (sinks_.empty()) return;
            for (auto &sink : sinks_) {
                sink->log(data, length);
            }
        }
    };
    class AsyncLogger : public Logger {
    public:
        AsyncLogger(std::string &logger_name, 
            LogLevel::Value limit_level, 
            Formatter::ptr &formatter,
            std::vector<LogSink::ptr> &sinks,
            AsyncType async_type = AsyncType::ASYNC_SAFE)
            : Logger(logger_name, limit_level, formatter, sinks),
            looper_(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::real_log, this, std::placeholders::_1), async_type)) {}
        virtual void log(const char *data, size_t length) override {
            // 这里looper_是线程安全的，不需要再加锁保护
            looper_->push(data, length);
        }
        void real_log(Buffer &buf) {
            // 这里不需要加锁，因为
            if (sinks_.empty()) return;
            for (auto &sink : sinks_) {
                sink->log(buf.begin(), buf.readable_size());
            }
        }
    private:
        // 使用智能指针管理 日志消息处理器&&日志工作器
        AsyncLooper::ptr looper_;
    };

    enum class LoggerType {
        SYNC_LOGGER,
        ASYNC_LOGGER
    };
    /* 为什么使用建造者模式？因为直接进行日志器的构建，需要用户设置一系列参数再进行传入才行。
       建造者模式中不使用指挥者的原因？日志器的构建的参数没有依赖关系，不需要按照特定的顺序构建，所以不需要指挥者保证顺序
    使用建造者模式来建造日志器，而不要让用户直接去构造日志器，简化用户创建日志器的复杂度
        1. 抽象一个日志器建造者类
            a.设置日志器类型
            b.讲不同类型日志器的创建放到同一个日志器建造者类中完成
        2.派生出具体的建造者类--局部日志器的建造者 & 全局日志器建造者
            在添加了全局单例管理器之后，将日志器添加全局管理
    
    */
    class LoggerBuilder {
    public:
        LoggerBuilder()
            :logger_type_(LoggerType::SYNC_LOGGER),
            limit_level_(LogLevel::Value::DEBUG),
            async_type_(AsyncType::ASYNC_SAFE) {}
    public:
        void build_logger_type(LoggerType logger_type) { logger_type_ = logger_type; }
        void build_logger_name(const std::string &logger_name) { logger_name_ = logger_name; }
        void build_log_level(LogLevel::Value level) { limit_level_ = level; }
        void build_Formatter(const std::string &pattern) {
            formatter_ = std::make_shared<Formatter>(pattern);
        }
        void build_enable_unsafe_async() { async_type_ = AsyncType::ASYNC_UNSAFE; }
        // 由于落地类型不同且不同落地类型的构造函数所需参数也不同，所以需要不定参
        template <typename SinkType, typename ...Args>
        void build_sink(Args &&...args) {
            LogSink::ptr sink = std::make_shared<SinkType>(std::forward<Args>(args)...);
            sinks_.push_back(sink);
        }
        virtual Logger::ptr build() = 0;
    protected:
        LoggerType logger_type_;// 日志器类型
        std::string logger_name_;// 日志器名称
        LogLevel::Value limit_level_;// 日志限制等级
        Formatter::ptr formatter_;// 格式化器
        std::vector<LogSink::ptr> sinks_;// 落地器
        AsyncType async_type_;// 异步日志器 安全选项
    };
    /* 建造局部日志器
    
    */
    class LocalLoggerBuilder : public LoggerBuilder {
    public:
        virtual Logger::ptr build() override {
            assert(!logger_name_.empty());
            if (sinks_.empty()) {
                build_sink<StdoutLogSink>();
            }
            if (!formatter_.get()) formatter_ = std::make_shared<Formatter>();
            if (logger_type_ == LoggerType::ASYNC_LOGGER) {
                return std::make_shared<AsyncLogger>(
                    logger_name_, 
                    limit_level_,
                    formatter_,
                    sinks_,
                    async_type_
                    );
            }
            return std::make_shared<SyncLogger>(logger_name_, limit_level_, formatter_, sinks_);
        }
    };

    class LoggerManager {
    public:
        static LoggerManager& get_instance() {
            // C++11以及之后的版本，编译器保证静态局部变量的创建是线程安全的
            // 即在静态局部变量创建过程中，其他线程将会阻塞，直到创建完成
            static LoggerManager singleton;
            return singleton;
        }
        // 添加日志器到日志管理类中
        void add_logger(const Logger::ptr logger) {
            if(has_logger(logger->get_name())) return;
            std::unique_lock<std::mutex> lock(mtx_);
            loggers_.insert(std::make_pair(logger->get_name(), logger));
        }
        // 日志器是否存在
        bool has_logger(const std::string name) {
            std::unique_lock<std::mutex> lock(mtx_);
            auto it = loggers_.find(name);
            if (it == loggers_.end()) return false;
            return true;
        }
        // 获取指定名称日志器，不存在返回空对象Logger::ptr()
        // 返回默认标准输出日志器
        Logger::ptr get_logger(const std::string name = DEFAULT_LOGGER) {
            std::unique_lock<std::mutex> lock(mtx_);
            std::unordered_map<std::string, Logger::ptr>::iterator it = loggers_.find(name);
            if (it == loggers_.end()) return Logger::ptr();
            return it->second;
        }
        // 返回默认标准输出日志器
        // Logger::ptr get_logger() {
        //     std::unique_lock<std::mutex> lock(mtx_);
        //     return default_logger_;
        // }
    private:
        //构造，建造标准输出日志器，作为默认日志器
        LoggerManager() {
            // 构造函数这里只能使用局部建造者类LocalLoggerBuilder，不能使用全局建造者类GlobalLoggerBuilder
            // 否则会涉及到循环创建的问题，因为全局建造者类调用了本类，如果本类构造又调用了全局建造者类，就相互依赖了
            std::unique_ptr<log::LoggerBuilder> builder(new log::LocalLoggerBuilder);
            builder->build_logger_name(DEFAULT_LOGGER);
            default_logger_ = builder->build();
            loggers_.insert(std::make_pair(DEFAULT_LOGGER, default_logger_));
        } 
        LoggerManager(const LoggerManager &) = delete;
        LoggerManager& operator=(const LoggerManager &) = delete;
    private:
        std::mutex mtx_;// 对loggers_的访问需要互斥
        std::unordered_map<std::string, Logger::ptr> loggers_;// 管理的所有日志器
        Logger::ptr default_logger_;// 未添加日志器时，默认就有的标准输出日志器，方便用户使用
    };

    /* 建造全局日志器
    
    */
    class GlobalLoggerBuilder : public LoggerBuilder {
    public:
        virtual Logger::ptr build() override {
            assert(!logger_name_.empty());
            
            if (sinks_.empty()) {
                build_sink<StdoutLogSink>();
            }
            if (!formatter_.get()) formatter_ = std::make_shared<Formatter>();
            Logger::ptr logger;
            if (logger_type_ == LoggerType::ASYNC_LOGGER) {
                logger = std::make_shared<AsyncLogger>(
                    logger_name_, 
                    limit_level_,
                    formatter_,
                    sinks_,
                    async_type_
                    );
            }
            else {
                logger = std::make_shared<SyncLogger>(
                    logger_name_, 
                    limit_level_, 
                    formatter_, 
                    sinks_
                    );
            }
            LoggerManager::get_instance().add_logger(logger);
            return logger;
        }
    };
}

#endif